/***************************************************************************
 *   Copyright (C) 2008/2009/2014 by Philipp Müller                        *
 *   philipp.mueller@gmx.de                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA          *
 ***************************************************************************/

//#include <QtDebug>
//#include <QCoreApplication>
//#include <QPainter>
#include <QPixmap>

#include "channellistmodel.h"
#include "vompremote.h"


ChannelListModel::ChannelListModel( VompRemote * mainWindow, QObject *parent ) : QAbstractListModel( parent ),
    m_mainWindow( mainWindow )
{
}


int ChannelListModel::rowCount( const QModelIndex &/*parent*/ ) const
{
//    qDebug() << "ChannelListModel::rowCount" << m_mainWindow->channelList()->count();
    return m_mainWindow->channelList()->count();
}


int ChannelListModel::columnCount( const QModelIndex &/*parent*/ ) const
{
    return ChannelListModel::ChannelColumnsLast;
}


QVariant ChannelListModel::data( const QModelIndex &index, int role ) const
{
//    qDebug() << "ChannelListModel::data role:" << role;
    if ( !index.isValid() )
        return QVariant();

    if ( index.row() >= m_mainWindow->channelList()->size() )
        return QVariant();

    switch ( role )
    {
        case Qt::EditRole: //Requested from Live-TV epg through QDataWidgetMapper
        {
            if ( index.column() == ChannelListModel::ChannelName )
            {
                return m_mainWindow->channelList()->at( index.row() ).name;
            }

            if ( index.column() == ChannelListModel::ChannelNumber )
            {
                return m_mainWindow->channelList()->at( index.row() ).channel;
            }

            if ( index.column() == ChannelListModel::ChannelIcon )
            {
                QString path = QCoreApplication::applicationDirPath() +
                               "/images/logos/" +
                               m_mainWindow->channelList()->at( index.row() ).name + ".png";
                return QPixmap( path );
            }

            if ( index.column() == ChannelListModel::EpgTime )
            {
                if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                {
                    return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].time.toString( "hh:mm" );
                }
                else
                {
                    return QVariant();
                }
            }

            if ( index.column() == ChannelListModel::EpgTitle )
            {
                updateEpgStatus( index.row() );

                if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                {
                    return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].title;
                }
                else
                {
                    return QVariant();
                }
            }

            return QVariant();
        }

        case Qt::DisplayRole:
        case ChannelListModel::ChannelNameRole:
        {
            return m_mainWindow->channelList()->at( index.row() ).name;
        }

//        case Qt::ToolTipRole:
//        {
//            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
//            {
//                if( m_mainWindow->centralWidget()->currentIndex() != MainWindow::WIDGET_CHANNELS )
//                {
//                    //TODO show the tooltip in the epg view as well
//                    return QVariant();
//                }

//                QString tip = m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].description;

//                if ( tip.isEmpty() )
//                {
//                    return QString( tr( "No description available" ) );
//                }

//                //We want max 80 Characters per line. Otherwise it is getting too wide.
//                //We check first for ": " then for ". ", etc for a line break
//                //TODO Move this into a global function
//                static const int maxChars = 80;
//                tip.remove( "\r" ); //No MS returns

//                QStringList tipList = tip.split( "\n" );
//                int round = 0; //Just safety for endless loops
//                int found;

//                for ( int i = 0; i < tipList.size(); ++i )
//                {
//                    while ( ( tipList[ i ].length() > maxChars ) && ( round < 500 ) )
//                    {
//                        //Check for ":"
//                        found = tipList[ i ].lastIndexOf( ": ", maxChars );
//                        if ( found > maxChars / 3 )
//                        {
//                            tipList.insert( i + 1, tipList[ i ].mid( found + 2 ) );
//                            tipList[ i ] = tipList[ i ].left ( found + 1 );
//                        }
//                        else
//                        {
//                            //Check for "?"
//                            found = tipList[ i ].lastIndexOf( "? ", maxChars );
//                            if ( found > maxChars / 3 )
//                            {
//                                tipList.insert( i + 1, tipList[ i ].mid( found + 2 ) );
//                                tipList[ i ] = tipList[ i ].left ( found + 1 );
//                            }
//                            else
//                            {
//                                //Check for "!"
//                                found = tipList[ i ].lastIndexOf( "! ", maxChars );
//                                if ( found > maxChars / 2 )
//                                {
//                                    tipList.insert( i + 1, tipList[ i ].mid( found + 2 ) );
//                                    tipList[ i ] = tipList[ i ].left ( found + 1 );
//                                }
//                                else
//                                {
//                                    //Check for "."
//                                    found = tipList[ i ].lastIndexOf( ". ", maxChars );
//                                    //We may have something like a data, so ignore it if the character before is a number.
//                                    if ( ( found > 0 ) &&
//                                        ( tipList[ i ].at( found - 1 ) >= QChar('0') ) &&
//                                        ( tipList[ i ].at( found - 1 ) <= QChar('9') ) )
//                                    {
//                                        found = 0;
//                                    }

//                                    if ( found > maxChars / 2 )
//                                    {
//                                        tipList.insert( i + 1, tipList[ i ].mid( found + 2 ) );
//                                        tipList[ i ] = tipList[ i ].left ( found + 1 );
//                                    }
//                                    else
//                                    {
//                                        //Check for " "
//                                        found = tipList[ i ].lastIndexOf( " ", maxChars );
//                                        if ( found > 0 )
//                                        {
//                                            tipList.insert( i + 1, tipList[ i ].mid( found + 1 ) );
//                                            tipList[ i ] = tipList[ i ].left ( found );
//                                        }
//                                        else
//                                        {
//                                            //Default: We need to cut in the middle of a word
//                                            found = maxChars;
//                                            if ( found < tipList[ i ].length() )
//                                            {
//                                                tipList.insert( i + 1, tipList[ i ].mid( found + 1 ) );
//                                                tipList[ i ] = tipList[ i ].left ( found );
//                                            }
//                                        }
//                                    }
//                                }
//                            }
//                        }
//                        round++;
//                        if ( round == 500 )
//                        {
//                            qWarning() << "ChannelListModel::data: endless loop in wordwrap calculation of tooltip";
//                        }
//                    }
//                }

//                tip = tipList.join( "\n" );
//                return tip;
//            }
//            else
//            {
//                return QVariant();
//            }
//        }

        case ChannelListModel::ChannelNumberRole:
        {
            return m_mainWindow->channelList()->at( index.row() ).channel;
        }


        case ChannelEpgTriggerUpdate:
        {
            updateEpgStatus( index.row() );

            return QVariant();
        }


        case ChannelListModel::ChannelEpgTitleRole:
        {
            updateEpgStatus( index.row() );
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
            {
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].title;
            }
            else
            {
                return QVariant();
            }
        }

        case ChannelListModel::ChannelEpgTitleNextRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 1 )
            {
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 1 ].title;
            }
            else
            {
                return QVariant();
            }
        }

        case ChannelListModel::ChannelEpgSubTitleRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].subTitle;
            else
                return QVariant();
        }

        case ChannelListModel::ChannelEpgDescriptionRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].description;
            else
                return QVariant();
        }

        case ChannelListModel::ChannelEpgTimeRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].time;
            else
                return QVariant();
        }

        case ChannelListModel::ChannelEpgTimeFromToRole:
        {
            return timeFromTo( index.row(), 0 );
        }

        case ChannelListModel::ChannelEpgDurationRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
            {
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].duration;
            }
            else
                return QVariant();
        }

        case ChannelListModel::ChannelEpgTimeFromRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
            {
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].time.toString( "hh:mm" );
            }
            else
                return QVariant();
        }

        case ChannelListModel::ChannelEpgTimeFromNextRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 1 )
            {
                return m_mainWindow->channelList()->at( index.row() ).scheduleList[ 1 ].time.toString( "hh:mm" );
            }
            else
            {
                return QVariant();
            }
        }

        case ChannelListModel::ChannelEpgProgressRole:
        {
            if ( m_mainWindow->channelList()->at( index.row() ).scheduleList.size() > 0 )
            {
                QDateTime epgTime     = m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].time;
                quint32   epgDuration = m_mainWindow->channelList()->at( index.row() ).scheduleList[ 0 ].duration;
                QDateTime serverTime  = m_mainWindow->serverTime();

                double progress = 1.0 * ( serverTime.toTime_t() - epgTime.toTime_t() ) / epgDuration;
                return progress;
            }
            else
                return QVariant();
        }

//        case Qt::DecorationRole:
//        {
//            QString path = QCoreApplication::applicationDirPath() +
//                           "/images/logos/" +
//                           m_mainWindow->channelList()->at( index.row() ).name + ".png";
//            return QVariant( QIcon( path ) );
//        }

        default:
        {
            return QVariant();
        }
    }
}


bool ChannelListModel::insertRows( int position, int rows, const QModelIndex &/*parent*/ )
{
    if ( position > m_mainWindow->channelList()->count() )
    {
        qDebug() << "ChannelListModel::insertRows position out of range with:" << position << "  and count:" << m_mainWindow->channelList()->count() - 1;
        return false;
    }

    beginInsertRows( QModelIndex(), position, position + rows - 1 );

    for ( int row = 0; row < rows; ++row )
    {
        ChannelEntry entry;
        m_mainWindow->channelList()->insert( position, entry );
    }

    endInsertRows();

    return true;
}


bool ChannelListModel::removeRows( int position, int rows, const QModelIndex &/*parent*/ )
{
    if ( rows <= 0 || rows + position > m_mainWindow->channelList()->count() )
    {
        qDebug() << "ChannelListModel::removeRows numbers out of range position:" << position
                 << "  rows:" << rows
                 << "  count:" << m_mainWindow->channelList()->count();
        return false;
    }

    beginRemoveRows( QModelIndex(), position, position + rows - 1 );

    for ( int row = 0; row < rows; ++row )
    {
        m_mainWindow->channelList()->removeAt( position );
    }

    endRemoveRows();

    return true;
}


void ChannelListModel::epgDataChanged( int row )
{
    if ( row < 0 )
    {
        emit layoutChanged();
    }
    else
    {
        emit dataChanged( index( row, 0 ), index( row, 0 ) );
    }
}


void ChannelListModel::updateEpgStatus( int row ) const
{
    bool updateEpg = false;
    quint32 duration = 60 * 60 * 24 * 8; // get 8 days into the future

    if ( row > m_mainWindow->channelList()->count() - 1 )
    {
        qWarning() << "ChannelListModel::updateEpgStatus row:" << row << " > list.count (- 1)" << m_mainWindow->channelList()->count() - 1;
        return;
    }

    //When we fresh start, we just need to pull data
    if ( m_mainWindow->channelList()->at( row ).scheduleListLastUpdate < m_mainWindow->serverTime() )
    {
        updateEpg = true;
    }

    //After duration of first entry, we need to refresh data
    if ( m_mainWindow->channelList()->at( row ).scheduleList.size() > 0 )
    {
        if ( m_mainWindow->channelList()->at( row ).scheduleList[ 0 ].duration > 0 )
        {
            duration = ( m_mainWindow->channelList()->at( row ).scheduleList[ 0 ].duration ) * 60 + 1;
            if ( m_mainWindow->channelList()->at( row ).scheduleList[ 0 ].time.addSecs( duration ) < m_mainWindow->serverTime() )
            {
                updateEpg = true;
            }
        }
    }

    if ( updateEpg )
    {
//         qDebug() << "ChannelListModel::updateEpgStatus with row = " << row;
        ( *m_mainWindow->channelList() )[ row ].scheduleListLastUpdate = m_mainWindow->serverTime().addSecs( 300 );

        emit getSchedule( ( *m_mainWindow->channelList() )[ row ].channel, m_mainWindow->serverTime(), duration );
    }
}


void ChannelListModel::updateSchedule( quint32 channel, const ScheduleList & scheduleList )
{
//     qDebug() << "ChannelListModel:updateSchedule: size = " << schedules.size() << " and channel" << channel;

    int row = m_mainWindow->channelList()->rowFromChannel( channel );
    ( *m_mainWindow->channelList() )[ row ].scheduleList.clear();

    for ( int i = 0; i < scheduleList.size(); ++i )
    {
        int row = m_mainWindow->channelList()->rowFromChannel( channel );

        if ( row > m_mainWindow->channelList()->count() - 1 || row < 0 )
        {
            qWarning() << "ChannelListModel::setSchedule: row is out of range: row=" << row;
            return;
        }

        ( *m_mainWindow->channelList() )[ row ].scheduleList.append( scheduleList[ i ] );
    }

    emit dataChanged( index( row, 0 ), index( row, 0 ) );

    //Do we need to update epgChannel?
//    QModelIndexList selectionList = m_mainWindow->channelListSelectionModel()->selectedIndexes();
//    if ( selectionList.size() != ChannelListModel::ChannelColumnsLast )
//    {
//        return;
//    }

//    if ( m_mainWindow->channelListSelectionModel()->selectedIndexes()[ 0 ].row() == row )
//    {
//        emit epgListChanged( row );
//    }
}


void ChannelListModel::triggerEpgUpdate( int channelItemFrom, int channelItemTo )
{
     if ( channelItemFrom < 0 || channelItemTo > m_mainWindow->channelList()->count() - 1 || channelItemFrom > channelItemTo )
     {
         qWarning() << "triggerEpgUpdate: channel out of range from:" << channelItemFrom << "  to:" << channelItemTo;
         return;
     }

    qDebug() << "triggerEpgUpdate received  from:" << channelItemFrom << "  to: " << channelItemTo;
    for ( int i = channelItemFrom; i <= channelItemTo; ++i )
    {
        updateEpgStatus( i );
    }

    return;
}


QString ChannelListModel::timeFromTo( int row, int item ) const
{
    if ( row < m_mainWindow->channelList()->size() )
    {
        if ( item < m_mainWindow->channelList()->at( row ).scheduleList.size() )
        {
            QDateTime epgTime      = m_mainWindow->channelList()->at( row ).scheduleList[ item ].time;
            quint32 epgDuration    = m_mainWindow->channelList()->at( row ).scheduleList[ item ].duration;

            if ( epgDuration > 0 )
            {
                return epgTime.toString( "hh:mm" ) + " - " + epgTime.addSecs( epgDuration ).toString( "hh:mm" );
            }
        }
    }

    return QString();
}

QHash<int, QByteArray> ChannelListModel::roleNames() const
{
    QHash<int, QByteArray> roles;
        roles[ChannelNumberRole]          = "channelNumber";
        roles[ChannelNameRole]            = "channelName";
        roles[ChannelEpgTitleRole]        = "channelEpgTitle";
        roles[ChannelEpgTitleNextRole]    = "channelEpgTitleNext";
        roles[ChannelEpgSubTitleRole]     = "channelEpgSubTitle";
        roles[ChannelEpgDescriptionRole]  = "channelEpgDescription";
        roles[ChannelEpgDurationRole]     = "channelEpgDuration";
        roles[ChannelEpgProgressRole]     = "channelEpgProgress";

        roles[ChannelEpgTimeRole]         = "channelEpgDuration";
        roles[ChannelEpgTimeFromToRole]   = "channelEpgTimeFromTo";
        roles[ChannelEpgTimeFromRole]     = "channelEpgTimeFrom";
        roles[ChannelEpgTimeFromNextRole] = "channelEpgTimeFromNext";

        roles[EpgCurrentTime]             = "epgCurrentTime";
        roles[ChannelEpgTriggerUpdate]    = "epgTriggerUpdate";
//        EpgCurrentSelectionTime,
    return roles;
}

